LiveData 是 Android 中一個很有用的項目,它是一種可觀察(Observe)的資料存儲器類(data holder)。它會感知 Android 的生命週期,也就是說當 Activity 或是 Fragment 在 STARTED
或 RESUMED
時,所被觀察的資料才會是在啟動狀態(active state)的。
在 MVVM 架構中,我們將架構分成 Model - ViewModel - View,而 LiveData 就是負責在 ViewModel 與 View 中的資料傳遞。而 LiveData 存放的則是 UI 介面上所需要的資料,所以它對於資料的更新需要具有敏感度,當 LiveData 內的資料更新時,View 層應該就要能夠立刻更新,減少手動呼叫。
View 層透過觀察 ViewModel 層暴露出的 LiveData,當資料變更的時候,就會自動通知 View 層更新畫面。
先定義 ViewModel 層,這一層主要是要決定哪些資料要暴露給 View 層,以及要這些資料要從哪邊取得,所以 ViewModel 是介於 View 與 Model 的。
class MyViewModel : ViewModel() {
private var _data = MutableLiveData<List<Driver>>()
val data: LiveData<List<Driver>> = _data
...
init{
val result = ...
_data.value = result
}
}
class MyFragment: Fragment(){
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.data.observe(viewLifecycleOwner, {
setText(it)
})
}
...
}
這邊我們就完成了一個簡單的 LiveData 。回到我們的主題 Flow,我們有沒有可能使用 Flow 來替代 LiveData 呢?
首先,我們先來分析一下 LiveData。 LiveData 可以讓 View 層「觀察」,等到資料更新時,就會把更新的資料通知給 View 層,所以在 View 層就不需要主動去查詢現在的值是什麼,只需要靜靜的等待即可。Observe and forget。
我們複習一下兩種 Flow 的用法,Shared Flow 可以與多個訂閱者分享資料,當有新的訂閱者加入時,會把最後面的幾筆資料傳給新訂閱者。之後每次有新的資料的時候,就會同時發送給所有訂閱者。而 State Flow 則是 Shared Flow 的特例,它每次建立的時候一定會有值,而在每次有新訂閱者加入時,只會把最後一筆的資料發送給新訂閱者,因為 StateFlow 只會儲存一個值。
這邊我們選擇 StateFlow 來作為 LiveData 的替代品。
將上面的 ViewModel 改用 StateFlow 替代
class MyViewModel : ViewModel() {
private var _data = MutableStateFlow<List<Driver>>(emptyList())
val data = _data.asStateFlow()
...
init{
val result = ...
_data.value = result
}
}
class MyFragment: Fragment(){
private var updatesJob: Job? = null
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
updatesJob = lifecycleScope.launch {
viewModel._data.collect {
setText()
}
}
}
...
override fun onDestroy() {
super.onDestroy()
updatesJob?.cancel()
}
}
可以發現,我們除了將 LiveData 用 Flow 取代之外,我們在 View 層還必須要使用 lifecycleScope.launch
來把 StateFlow 包裝起來,因為 collect
是一個 suspend 函式。
為了避免在離開的時候還繼續收資料,必須要在離開畫面的時候把 Job 給停掉。
不過在 lifecycle:lifecycle-runtime-ktx:2.4.0
會改,但是目前還是只有 alpha 版而已。
Flow 能夠替代 LiveData 是肯定的,不過因為 LiveData 非常簡單使用,所以以目前來說不一定需要使用 Flow 來替代 LiveData 。畢竟 LiveData 與 Activity/Fragment 的生命週期綁在一起,當畫面結束的時候,訂閱的資料也就不會繼續更新。如果以目前的 Flow 來說,首先要思考的是我們在 View 層就需要建立一個 Scope 讓這個 Flow 跑在背景,等到結束畫面的時候,還要記得把它停掉。這樣子很容易就出錯了。
或許我們可以等到更穩定的時候,Flow 像現在的 LiveData 那麼實用的時候才來改。
Migrating from LiveData to Kotlin’s Flow
A safer way to collect flows from Android UIs
Kotlin Taiwan User Group
Kotlin 讀書會
有興趣的讀者歡迎參考:https://coroutine.kotlin.tips/
天瓏書局